use std::{mem::size_of, sync::Arc};

use log::info;
use nalgebra::{Matrix, Matrix4, Point3, Rotation3, UnitQuaternion, Vector3};
use serde::{Deserialize, Serialize};
use QcMacros::Comp;
use QcRender::{
    buffers::UniformBuffer,
    resources::{Mesh, Shader, Texture},
};

use crate::{ecs::components::transform::Transform, resources::material::Material};

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum LightType {
    Point = 0,
    Directional = 1,
    Spot = 2,
    AmbientBox = 3,
    AmbientSphere = 4,
}

#[derive(Debug, Comp, Clone, Serialize, Deserialize)]
pub struct Light {
    inner: ComponentInner,
    pub color: [f32; 3],
    pub intensity: f32,
    pub light_type: LightType,
    pub quadratic: f32,
    pub cutoff: f32,
    pub constant: f32,
    pub linear: f32,
    pub outer_cutoff: f32,
}

impl Light {
    pub fn new() -> Self {
        Self {
            inner: ComponentInner::default(),
            color: [1., 1., 1.],
            intensity: 1.,
            light_type: LightType::Point,
            quadratic: 1.,
            cutoff: 12.,
            constant: 0.,
            linear: 0.,
            outer_cutoff: 15.,
        }
    }

    pub fn with_intensity(mut self, intensity: f32) -> Self {
        self.intensity = intensity;
        self
    }

    pub fn with_type(mut self, light_type: LightType) -> Self {
        self.light_type = light_type;
        self
    }

    pub fn generate_matrix(&self, matrix: Matrix4<f32>, transform: &Transform) -> Matrix4<f32> {
        let mut result = Matrix4::identity();

        result[(0, 0)] = matrix[(0, 3)];
        result[(1, 0)] = matrix[(1, 3)];
        result[(2, 0)] = matrix[(2, 3)];

        let forward = transform.get_forward();
        result[(0, 1)] = forward.x;
        result[(1, 1)] = forward.y;
        result[(2, 1)] = forward.z;

        result[(0, 2)] = pack(self.color) as f32;

        result[(0, 3)] = self.light_type as i32 as _;
        result[(1, 3)] = self.cutoff as i32 as _;
        result[(2, 3)] = self.outer_cutoff as i32 as _;

        result[(3, 0)] = self.constant as i32 as _;
        result[(3, 1)] = self.linear as i32 as _;
        result[(3, 2)] = self.quadratic as i32 as _;
        result[(3, 3)] = self.intensity as i32 as _;
        result
    }
}

fn pack(color: [f32; 3]) -> u32 {
    let x = ((color[0] * 255.) as u32) << 24;
    let y = ((color[1] * 255.) as u32) << 16;
    let z = ((color[2] * 255.) as u32) << 8;

    x | y | z | 0
}
impl Default for Light {
    fn default() -> Self {
        Self {
            inner: ComponentInner::default(),
            color: [1., 1., 1.],
            intensity: 1.,
            light_type: LightType::Point,
            quadratic: 1.,
            cutoff: 12.,
            constant: 0.,
            linear: 0.,
            outer_cutoff: 15.,
        }
    }
}

#[test]
fn test_matrix() {
    let matrix = Matrix4::new_translation(&Vector3::new(1., 2., 3.));

    println!("{}", matrix);

    println!("{:?}", matrix[(0, 3)]);

    println!("{:?}", matrix[(1, 3)]);

    println!("{:?}", matrix[(2, 3)]);
}
